package com.qf.index;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BasisTable {

    // 1. 关键词出现多少次才可以认定为关键词呢？在测试程序中大于0即可，实际使用时，以大于50次为标准，设置在方法：insertKeyWordSumMap中（if (sum > 0 || (keyWordList.size() == 2 && sum > 0) || (keyWordList.size() == 3 && sum > 0)) {），
    // 2. keyWordList.size() == 2即是全写与缩写，数量可以设置为10次。keyWordList.size() == 3即是mesh词，数量可以设置为1次。中文的关键词，大于10次即可。
    public static Map<String, String> insertKeyWordSumMap(String keyWordTable, Map<List<String>, Integer> keyWordSumMap, Map<String, String> keyWordAndMeshMap, Map<String, String> md5Map, Map<String, Integer> cnKeyWordSumMap, Map<String, String> reKwMap) {
        Map<String, String> keyWordAndStandardMap = new ConcurrentHashMap<>();
        keyWordSumMap.keySet().parallelStream().forEach(keyWordList -> {
            Integer sum = keyWordSumMap.get(keyWordList);
            if (sum > 50 || (keyWordList.size() == 2 && sum > 10) || (keyWordList.size() == 3 && sum > 1)) {
                String kw = reRemaining(Utils.reKw(reKwMap, keyWordList.get(0)));
                String standard = keyWordAndMeshMap.getOrDefault(keyWordList.get(0), kw);
                En.InfoAndStandard infoAndStandard = getInfoAndStandard(kw, standard, md5Map, reKwMap);
                keyWordAndStandardMap.put(infoAndStandard.getInfo(), infoAndStandard.getStandard());
            }
            try {
                Db.use().insert(Entity.create(keyWordTable).set("key_word", JSONUtil.toJsonStr(keyWordList)).set("sum", keyWordSumMap.get(keyWordList)).set("is_cn", 0));
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
        keyWordSumMap.clear();

        cnKeyWordSumMap.keySet().parallelStream().forEach(cnKey -> {
            Integer sum = cnKeyWordSumMap.get(cnKey);
            try {
                Db.use().insert(Entity.create(keyWordTable).set("key_word", cnKey).set("sum", sum).set("is_cn", 1));
            } catch (SQLException e) {
                e.printStackTrace();
            }
            if (sum > 30) {
                En.InfoAndStandard infoAndStandard = getInfoAndStandard(cnKey, cnKey, md5Map, reKwMap);
                keyWordAndStandardMap.put(infoAndStandard.getInfo(), infoAndStandard.getStandard());
            }
        });
        cnKeyWordSumMap.clear();
        return keyWordAndStandardMap;
    }

    // 因英文下是以空格切割的，如果某个单词后面是一个句号，因切割后单词与句号是连在一起的，就无法匹配出来了，为了解决这个问题，需要双方（加MAP及匹配时）均对句号等给转换一下，存MAP时，如果info与standard值相等，则只插入info，已节约空间。
    public static En.InfoAndStandard getInfoAndStandard(String info, String standard, Map<String, String> md5Map, Map<String, String> reKwMap) {
        En.InfoAndStandard infoAndStandard = new En.InfoAndStandard();
        standard = (standard.isEmpty()) ? info : standard;
        standard = Utils.reKw(reKwMap, standard.replace("[Publication Type]", "").replace("[Subheading]", "").replace("[文献类型]", ""));
        info = reRemaining(info.replace("[Publication Type]", "").replace("[Subheading]", "").replace("[文献类型]", "")).replaceAll("( non )", " non-").replaceAll("(^non )", "non-")
                .replaceAll("( not )", " not-").replaceAll("(^not )", "not-").replace("非", "非-").replace("不", "不-").replaceAll("(.?-.?)", "㵝$1㵝");
        standard = standard.toLowerCase().replaceAll(" +", " ");
        standard = (info.equals(standard)) ? "☷" : standard;
        standard = ((info + ".").equals(standard)) ? "☷." : standard;
        standard = ((info + ",").equals(standard)) ? "☷," : standard;
        standard = ((info + ";").equals(standard)) ? "☷;" : standard;
        standard = ((info + "?").equals(standard)) ? "☷?" : standard;
        standard = ((info + "*").equals(standard)) ? "☷*" : standard;
        standard = (("(" + info + ").").equals(standard)) ? "☷(" : standard;
        standard = (("(" + info + ")").equals(standard)) ? "☷)" : standard;
        standard = (("[" + info + "].").equals(standard)) ? "☷[" : standard;
        standard = (("[" + info + "]").equals(standard)) ? "☷]" : standard;
        standard = (resetRemaining(info).equals(standard)) ? "ㅐ" : standard;
        standard = ((resetRemaining(info) + ".").equals(standard)) ? "ㅐ." : standard;
        standard = ((resetRemaining(info) + ",").equals(standard)) ? "ㅐ," : standard;
        standard = ((resetRemaining(info) + ";").equals(standard)) ? "ㅐ;" : standard;
        standard = ((resetRemaining(info) + "?").equals(standard)) ? "ㅐ?" : standard;
        standard = ((resetRemaining(info) + "*").equals(standard)) ? "ㅐ*" : standard;
        standard = (("(" + resetRemaining(info) + ").").equals(standard)) ? "ㅐ(" : standard;
        standard = (("(" + resetRemaining(info) + ")").equals(standard)) ? "ㅐ)" : standard;
        standard = (("[" + resetRemaining(info) + "].").equals(standard)) ? "ㅐ[" : standard;
        standard = (("[" + resetRemaining(info) + "]").equals(standard)) ? "ㅐ]" : standard;
        if (info.length() > 16) {
            String md5 = SecureUtil.md5(info).substring(8, 24).toLowerCase();
            md5Map.put(md5, info);
            info = md5;
        }
        int len = (Utils.isContainChinese(info)) ? 1 : 2;
        if (info.length() > len) {
            infoAndStandard.setInfo(info);
            infoAndStandard.setStandard(standard);
        }
        return infoAndStandard;
    }

    // 返回余下的，因为在匹配之前对文本作了操作，返回之前需要再次还原回来。
    public static String resetRemaining(String remaining) {
        remaining = remaining.replaceAll(" +", " ").replace(" 纃1 ", "|").replace(" 纃a ", ". ").replace(" 纃b ", " .").replace(" 纃c ", ".")
                .replace(" 纃d ", ", ").replace(" 纃e ", " ,").replace(" 纃f ", ",").replace(" 纃g ", "; ").replace(" 纃h ", " ;").replace(" 纃i ", ";")
                .replace(" 纃j ", "( ").replace(" 纃k ", " (").replace(" 纃l ", "(").replace(" 纃m ", ") ").replace(" 纃n ", " )").replace(" 纃o ", ")")
                .replace(" 纃p ", "/ ").replace(" 纃q ", " /").replace(" 纃r ", "/").replace(" 纃s ", "\" ").replace(" 纃t ", " \"").replace(" 纃u ", "\"")
                .replace(" 纃v ", ": ").replace(" 纃w ", " :").replace(" 纃x ", ":").replace(" 纃y ", "?").replace(" 纃z ", "*");
        remaining = remaining.replaceAll(" +", " ").replace("纃a", " ").replace("纃b", " ").replace("纃c", " ").replace("纃d", " ").replace("纃e", " ")
                .replace("纃f", " ").replace("纃g", " ").replace("纃h", " ").replace("纃i", " ").replace("纃j", " ").replace("纃k", " ")
                .replace("纃l", " ").replace("纃m", " ").replace("纃n", " ").replace("纃o", " ").replace("纃p", " ").replace("纃q", " ")
                .replace("纃r", " ").replace("纃s", " ").replace("纃t", " ").replace("纃u", " ").replace("纃v", " ").replace("纃w", " ")
                .replace("纃x", " ").replace("纃y", " ").replace("纃z", " ").replace("纃1", " ").replaceAll(" +", " ");
        return remaining;
    }

    public static String reEndStr(String info) {
        info = info.replaceAll(" +", " ").replace(" -", "-").replace("- ", "-");
        info = (info.endsWith(".")) ? info.substring(0, info.length() - ".".length()) : info;
        info = (info.endsWith(",")) ? info.substring(0, info.length() - ".".length()) : info;
        info = (info.endsWith(";")) ? info.substring(0, info.length() - ".".length()) : info;
        info = (info.endsWith("?")) ? info.substring(0, info.length() - "?".length()) : info;
        info = (info.endsWith("*")) ? info.substring(0, info.length() - "*".length()) : info;
        info = (info.startsWith("(") && info.endsWith(").")) ? info.substring("(".length(), info.length() - ").".length()) : info;
        info = (info.startsWith("(") && info.endsWith(")")) ? info.substring("(".length(), info.length() - ")".length()) : info;
        info = (info.startsWith("[") && info.endsWith("].")) ? info.substring("[".length(), info.length() - "].".length()) : info;
        info = (info.startsWith("[") && info.endsWith("]")) ? info.substring("[".length(), info.length() - "]".length()) : info;
        return info.replaceAll(" +", " ");
    }

    public static String reRemaining(String info) {
        // Particle formation by supercritical fluid extraction and expansion process. 通过超临界流体萃取和膨胀过程形成颗粒
        info = reEndStr(info);
        info = info.toLowerCase().replaceAll(" +", " ").replace("|", " 纃1 ").replace(". ", " 纃a ").replace(" .", " 纃b ").replace(")", " 纃o ").replace(".", " 纃c ")
                .replace(", ", " 纃d ").replace(" ,", " 纃e ").replace(",", " 纃f ").replace("; ", " 纃g ").replace(" ;", " 纃h ").replace(";", " 纃i ")
                .replace("( ", " 纃j ").replace(" (", " 纃k ").replace("(", " 纃l ").replace(") ", " 纃m ").replace(" )", " 纃n ")
                .replace("/ ", " 纃p ").replace(" /", " 纃q ").replace("/", " 纃r ").replace("\" ", " 纃s ").replace(" \"", " 纃t ").replace("\"", " 纃u ")
                .replace(": ", " 纃v ").replace(" :", " 纃w ").replace(":", " 纃x ").replace("?", " 纃y ").replace("*", " 纃z ").replaceAll(" +", " ");
        return info.replace("⟪", "(").replace("⟫", ")");
    }

    public static Map<String, String> readMerge(String jourMergeTable, int onePageNum, String limit, String autoId, String field, boolean print, int allTotal) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Map<String, String> nlmIdAndFullTitleMap = new ConcurrentHashMap<>();
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + jourMergeTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + jourMergeTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            AtomicInteger sumNum = new AtomicInteger(0);
            int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
            AtomicInteger startNum = new AtomicInteger(1);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + jourMergeTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                    if (print && sumNum.getAndIncrement() % showNum == 0) {
                        System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + jourMergeTable + Utils.printMemoryStr() + "\033[0m");
                    }
                    for (Entity entry : tableData) {
                        String ncNlmId = entry.getStr("nc_nlm_id");
                        String ncTitleS = entry.getStr("nc_biao_ti");
                        if (null != ncNlmId && null != ncTitleS && ncNlmId.length() > 1 && ncTitleS.length() > 3) {
                            nlmIdAndFullTitleMap.put(ncNlmId.toUpperCase().trim(), ncTitleS.trim());
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
        return nlmIdAndFullTitleMap;
    }

    public static Map<Integer, String> readHtml(String pmHtmlTable, int onePageNum, String limit, String autoId, String field, boolean print, int allTotal) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Map<Integer, String> pmIdAndEPubMap = new ConcurrentHashMap<>();
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + pmHtmlTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + pmHtmlTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            AtomicInteger sumNum = new AtomicInteger(0);
            int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
            AtomicInteger startNum = new AtomicInteger(1);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + pmHtmlTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                    if (print && sumNum.getAndIncrement() % showNum == 0) {
                        System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + pmHtmlTable + Utils.printMemoryStr() + "\033[0m");
                    }
                    for (Entity entry : tableData) {
                        Integer pmId = entry.getInt("pmid");
                        String ePub = entry.getStr("ePub");
                        if (null != pmId && null != ePub && pmId > 0 && ePub.length() > 4) {
                            pmIdAndEPubMap.put(pmId, ePub);
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
        return pmIdAndEPubMap;
    }

    public static void updateKeyWord(String readTable, int onePageNum, String limit, String autoId, String field, boolean print, int allTotal, Map<String, String> originalAndMeshMap, Map<String, String> keyWordAndStandardMap) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + readTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + readTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            AtomicInteger sumNum = new AtomicInteger(0);
            int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
            AtomicInteger startNum = new AtomicInteger(1);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + readTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                    if (print && sumNum.getAndIncrement() % showNum == 0) {
                        System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + readTable + Utils.printMemoryStr() + "\033[0m");
                    }
                    for (Entity entry : tableData) {
                        Integer aid = entry.getInt(autoId);
                        if (null != aid && aid > 0) {
                            String keywords = entry.getStr("keyword");
                            Set<List<String>> kwSetList = new HashSet<>();
                            if (null != keywords && keywords.length() > 3) {
                                String[] split = keywords.split("; ");
                                addSetList(originalAndMeshMap, new ArrayList<>(Arrays.asList(split)), kwSetList, false);
                            }

                            String title = entry.getStr("title");
                            Set<List<String>> tiSetList = new HashSet<>();
                            Set<String> tiKeySet = (null != title && title.length() > 4) ? getTiKey(title, keyWordAndStandardMap) : new HashSet<>();
                            addSetList(originalAndMeshMap, new ArrayList<>(tiKeySet), tiSetList, true);

                            Set<List<String>> abSetList = new HashSet<>();
                            String abbWord = entry.getStr("abb_word");
                            List<String> abbWordList = (null != abbWord && abbWord.contains("\"]")) ? JSONObject.parseArray(abbWord, String.class) : new ArrayList<>();
                            for (String lists : abbWordList) {
                                List<String> list = JSONObject.parseArray(lists, String.class);
                                abSetList.add(list);
                            }

                            String ab = entry.getStr("ab");
                            Set<String> abKeySet = (null != ab && ab.length() > 10) ? getAbKey(ab, keyWordAndStandardMap) : new HashSet<>();
                            Set<String> abSet = new HashSet<>();
                            for (String abKey : abKeySet) {
                                boolean bool = true;
                                for (List<String> list : abSetList) {
                                    if (list.contains(abKey)) {
                                        bool = false;
                                        break;
                                    }
                                }
                                if (bool) {
                                    abSet.add(abKey);
                                }
                            }
                            addSetList(originalAndMeshMap, new ArrayList<>(abSet), abSetList, true);

                            // 关键词基本上都是保持原有的样子不作改变，而从标题和摘要的，则要使用提取的程序。
                            String cnKeywords = entry.getStr("cn_kw");
                            if (null != cnKeywords && cnKeywords.length() > 3) {
                                addSetList(new HashSet<>(Arrays.asList(cnKeywords.split("; "))), kwSetList);
                            }

                            String cnTi = entry.getStr("cn_ti");
                            Set<String> cnTiKeySet = (null != cnTi && cnTi.length() > 4) ? getCnTiKey(cnTi, keyWordAndStandardMap) : new HashSet<>();
                            addSetList(cnTiKeySet, tiSetList);

                            String cnAb = entry.getStr("cn_ab");
                            Set<String> cnAbKeySet = (null != cnAb && cnAb.length() > 10) ? getCnAbKey(cnAb, keyWordAndStandardMap) : new HashSet<>();
                            addSetList(cnAbKeySet, abSetList);

                            Set<String> allSet = new HashSet<>();
                            addAllSet(tiSetList, allSet);
                            addAllSet(kwSetList, allSet);
                            addAllSet(abSetList, allSet);
                            try {
                                Db.use().update(
                                        Entity.create().set("ti_key", JSONUtil.toJsonStr(tiSetList))
                                                .set("kw_key", JSONUtil.toJsonStr(kwSetList))
                                                .set("ab_key", JSONUtil.toJsonStr(abSetList))
                                                .set("key_word_list", JSONUtil.toJsonStr(allSet))
                                        , Entity.create(readTable).set(autoId, aid));
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    public static void addSetList(Set<String> cnAbKeySet, Set<List<String>> abSetList) {
        for (String s : cnAbKeySet) {
            if (s.length() > 1) {
                List<String> list = new ArrayList<>();
                list.add(s);
                abSetList.add(list);
            }
        }
    }

    public static void addAllSet(Set<List<String>> tiSetList, Set<String> allSet) {
        for (List<String> list : tiSetList) {
            for (String s : list) {
                if (!"m".equals(s) && s.length() > 2) {
                    allSet.add(s);
                }
            }
        }
    }

    public static void readMesh(String readTable, int onePageNum, String limit, String autoId, String field, boolean print, int allTotal, Map<String, String> md5Map, Map<String, String> keyWordAndMeshMap, Map<String, String> originalAndMeshMap, Map<String, String> reKwMap) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + readTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + readTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            AtomicInteger sumNum = new AtomicInteger(0);
            int showNum = Math.max((tableList.size() / allTotal), 1) + (tableList.size() % allTotal);
            AtomicInteger startNum = new AtomicInteger(1);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + readTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                    if (print && sumNum.getAndIncrement() % showNum == 0) {
                        System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startNum.getAndIncrement() + " 总次数：" + allTotal + " 表名：" + readTable + Utils.printMemoryStr() + "\033[0m");
                    }
                    for (Entity entry : tableData) {
                        String meshName = Utils.getStrField(entry, "meshName").replace("[Publication Type]", "").replace("[Subheading]", "").replace("[文献类型]", "");
                        meshName = meshName.endsWith(".") ? meshName.substring(0, meshName.length() - 1) : meshName;
                        List<String> kwList = strToList(Utils.getStrField(entry, "entryTerms"));
                        kwList.add(meshName);
                        for (String original : kwList) {
                            original = original.endsWith(".") ? original.substring(0, original.length() - 1) : original;
                            String kw2 = reRemaining(Utils.reKw(reKwMap, original));
                            En.InfoAndStandard infoAndStandard = getInfoAndStandard(kw2, meshName, md5Map, reKwMap);
                            String key = infoAndStandard.getInfo();
                            if (key.length() > 4 && key.contains(" ")) {
                                keyWordAndMeshMap.put(key, meshName);
                                if (!original.toLowerCase().trim().equals(meshName.toLowerCase().trim())) {
                                    originalAndMeshMap.put(original.toLowerCase().trim(), meshName);
                                }
                            }
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    // 字符串转换为List后输出，参数len是对字符串长度的约束，一般来说List存表后的字符串应大于4位，比如：["图书馆"]
    public static List<String> strToList(String str) {
        if (null != str && str.length() > 4 && str.endsWith("\"]")) {
            try {
                List<String> list = JSONObject.parseArray(str, String.class);
                if (null != list && !list.isEmpty()) {
                    Set<String> set = new HashSet<>();
                    for (String s : list) {
                        if (!s.isEmpty()) {
                            set.add(s);
                        }
                    }
                    return new ArrayList<>(set);
                }
            } catch (Exception e) {
                System.out.println("\033[31;1m" + "--strToList err-- " + "\033[0m" + str);
            }
        }
        return new ArrayList<>();
    }

    public static List<String> getUtAuthList(String afStr, Set<String> pinYinSet) {
        List<String> authList = new ArrayList<>();
        // Chang-Ming Huang -> isOrder = ture 统一使用：Huang ChangMing
        if (null != afStr && afStr.length() > 3) {
            boolean isOrder = getPiYiOrder(afStr, pinYinSet);
            String[] split = afStr.split("; ");
            for (String auth : split) {
                if (isOrder && auth.lastIndexOf(" ") > 0) {
                    String end = auth.substring(0, auth.lastIndexOf(" "));
                    String start = auth.substring(auth.lastIndexOf(" "));
                    // 部分英文作者的名称，可能有单个字母的，不给予转换。
                    if (end.length() > 1 && start.length() > 1) {
                        auth = start + " " + end;
                    }
                }
                String s = changeAuthOrder(auth, pinYinSet);
                authList.add(s);
            }
        }
        return authList;
    }

    public static List<String> getPmAuthListOriginal(String authorExtend) {
        List<String> authList = new ArrayList<>();
        if (null != authorExtend && authorExtend.contains("Affiliation")) {
            JSONArray objects = JSONUtil.parseArray(authorExtend);
            for (Object object : objects) {
                JSON parse = JSONUtil.parse(object);
                String lastName = null != parse.getByPath("LastName") ? parse.getByPath("LastName").toString().trim() : "";
                String foreName = null != parse.getByPath("ForeName") ? parse.getByPath("ForeName").toString().trim() : "";
                if ("-".equals(foreName)) {
                    authList.add(lastName);
                } else {
                    authList.add(lastName + ", " + foreName);
                }
            }
        }
        return authList;
    }

    //PMID: 37599498->PMID: 28767204
    public static List<String> getPmAuthList(String authorExtend, Set<String> pinYinSet) {
        List<String> authList = new ArrayList<>();
        if (null != authorExtend && authorExtend.contains("Affiliation")) {
            JSONArray objects = JSONUtil.parseArray(authorExtend);
            List<String> authorList = new ArrayList<>();
            for (Object object : objects) {
                JSON parse = JSONUtil.parse(object);
                String lastName = null != parse.getByPath("LastName") ? parse.getByPath("LastName").toString().trim() : "";
                String foreName = null != parse.getByPath("ForeName") ? parse.getByPath("ForeName").toString().trim() : "";
                if ("-".equals(foreName)) {
                    authorList.add(lastName);
                } else {
                    authorList.add(lastName + ", " + foreName);
                }
            }
            // Chang-Ming Huang -> isOrder = ture 统一使用：Huang ChangMing
            boolean isOrder = getPiYiOrder(CollUtil.join(authorList, "; "), pinYinSet);
            for (Object object : objects) {
                JSON parse = JSONUtil.parse(object);
                String lastName = null != parse.getByPath("LastName") ? parse.getByPath("LastName").toString().trim() : "";
                String foreName = null != parse.getByPath("ForeName") ? parse.getByPath("ForeName").toString().trim() : "";
                if ("-".equals(foreName)) {
                    String s = changeAuthOrder(lastName, pinYinSet);
                    authList.add(s);
                } else {
                    String auth = lastName + " " + foreName.replace("  ", " ").trim();
                    if (isOrder && auth.lastIndexOf(" ") > 0) {
                        String end = auth.substring(0, auth.lastIndexOf(" "));
                        String start = auth.substring(auth.lastIndexOf(" "));
                        // 部分英文作者的名称，可能有单个字母的，不给予转换。
                        if (end.length() > 1 && start.length() > 1) {
                            auth = start + " " + end;
                        }
                    }
                    String s = changeAuthOrder(auth, pinYinSet);
                    authList.add(s);
                }
            }
        }
        return authList;
    }

    //  ChangMing Huang
    public static boolean isPinYinAuth(String auth, Set<String> pinYinSet) {
        auth = auth.replace("-", " ").replace(".", " ").replace(",", " ").replaceAll(" +", " ").toLowerCase().trim();
        String[] split = auth.split(" ");
        if (split.length == 2) {
            String a = split[0];
            String b = split[1];
            if (pinYinSet.contains(a) && pinYinSet.contains(b)) {
                return true;
            }
            if (pinYinSet.contains(b) && a.length() > 3 && !pinYinSet.contains(a)) {
                for (int i = 2; i < a.length(); i++) {
                    String firstName = a.substring(0, i);
                    String lastName = a.substring(i);
                    if (pinYinSet.contains(firstName) && pinYinSet.contains(lastName)) {
                        return true;
                    }
                }
            }
            if (pinYinSet.contains(a) && b.length() > 3 && !pinYinSet.contains(b)) {
                for (int i = 2; i < b.length(); i++) {
                    String firstName = b.substring(0, i);
                    String lastName = b.substring(i);
                    if (pinYinSet.contains(firstName) && pinYinSet.contains(lastName)) {
                        return true;
                    }
                }
            }
        }
        if (split.length == 3) {
            return pinYinSet.contains(split[0]) && pinYinSet.contains(split[1]) && pinYinSet.contains(split[2]);
        }
        return false;
    }

    //  ChangMing Huang  zhuang bing -bo
    public static String changeAuthOrder(String auth, Set<String> pinYinSet) {
        auth = auth.replace(" -", "-").replace("- ", "-").replace(".", " ").replace(",", " ").replaceAll(" +", " ").trim();
        String[] split = auth.split(" ");
        if (split.length == 2) {
            String a = split[0];
            String b = split[1];
            if (pinYinSet.contains(b.toLowerCase()) && a.contains("-") && !a.endsWith("-") && !a.startsWith("-")) {
                String[] list = a.split("-");
                if (list.length == 2) {
                    if (pinYinSet.contains(list[0].toLowerCase()) && pinYinSet.contains(list[1].toLowerCase())) {
                        return b + " " + list[0] + "-" + list[1]; // Qi-Yue Chen
                    }
                }
            }
            if (pinYinSet.contains(a.toLowerCase()) && b.contains("-") && !b.endsWith("-") && !b.startsWith("-")) {
                String[] list = b.split("-");
                if (list.length == 2) {
                    if (pinYinSet.contains(list[0].toLowerCase()) && pinYinSet.contains(list[1].toLowerCase())) {
                        return a + " " + list[0] + "-" + list[1]; // Chen Qi-Yue
                    }
                }
            }
            if (pinYinSet.contains(b.toLowerCase()) && a.length() > 3 && !pinYinSet.contains(a.toLowerCase())) {
                for (int i = 2; i < a.length(); i++) {
                    String firstName = a.substring(0, i);
                    String lastName = a.substring(i);
                    if (pinYinSet.contains(firstName.toLowerCase()) && pinYinSet.contains(lastName.toLowerCase())) {
                        return b + " " + a; // QiYue Chen
                    }
                }
            }
            if (a.length() == 1 && b.length() > 1) {
                return b + " " + a; // y zhong
            }
        }
        if (split.length == 3) {
            String a = split[0];
            String b = split[1];
            String c = split[2];
            if (pinYinSet.contains(a.toLowerCase()) && pinYinSet.contains(b.toLowerCase()) && pinYinSet.contains(c.toLowerCase())) {
                return split[0] + " " + split[1] + split[2];
            }
            if (a.length() == 1 && b.length() == 1 && c.length() > 1) {
                return c + " " + a + " " + b; // y h yang
            }
        }
        return auth;
    }

    // Ru-Hong Tu; Chang-Ming Huang  英文名字中，新志贾这类属于正序，反之贾新志这类属于反序，程序默认为正序转反序，如果是反序则不用再转换了。对于正序返回true，没有一条是正序，则返回false。
    public static boolean getPiYiOrder(String piYinAuth, Set<String> pinYinSet) {
        // Ru-Hong Tu; Chang-Ming Huang  英文名字中，新志贾这类属于正序，反之贾新志这类属于反序，程序默认为正序转反序，如果是反序则不用再转换了。
        Matcher matcher = Pattern.compile("([a-zA-Z])").matcher(piYinAuth);
        if (matcher.find()) {
            String[] split = piYinAuth.toLowerCase().split(";");
            for (String auth : split) {
                String[] list = auth.replace(",", " ").trim().replace("  ", " ").split(" ");
                if (list.length == 2) {
                    String a = list[0];
                    String b = list[1];
                    if (pinYinSet.contains(a) && b.contains("-") && !b.startsWith("-") && !b.endsWith("-")) {
                        String[] end = b.split("-");
                        if (end.length == 2 && pinYinSet.contains(end[0]) && pinYinSet.contains(end[1])) {
//                            System.out.println("1 " + auth);
                            return false; // Chen, Qi-Yue
                        }
                    }
                    if (pinYinSet.contains(b) && a.contains("-") && !a.startsWith("-") && !a.endsWith("-")) {
                        String[] end = a.split("-");
                        if (end.length == 2 && pinYinSet.contains(end[0]) && pinYinSet.contains(end[1])) {
//                            System.out.println("2 " + auth);
                            return true; // Jian-Xian, Lin
                        }
                    }
                }
            }

            for (String auth : split) {
                String[] list = auth.replace("  ", " ").trim().split(",");
                if (list.length == 2) {
                    String a = list[0].replace(" ", "").trim();
                    String b = list[1].replace(" ", "").trim();
                    if (pinYinSet.contains(a) && b.length() > 3 && !pinYinSet.contains(b)) {
                        for (int i = 2; i < b.length(); i++) {
                            String firstName = b.substring(0, i);
                            String lastName = b.substring(i);
                            if (pinYinSet.contains(firstName) && pinYinSet.contains(lastName)) {
//                                System.out.println("3 " + auth);
                                return false; // Chen, QiYue
                            }
                        }
                    }
                    if (pinYinSet.contains(b) && a.length() > 3 && !pinYinSet.contains(a)) {
                        for (int i = 2; i < a.length(); i++) {
                            String firstName = a.substring(0, i);
                            String lastName = a.substring(i);
                            if (pinYinSet.contains(firstName) && pinYinSet.contains(lastName)) {
//                                System.out.println("4 " + auth);
                                return true; // JianXian, Lin
                            }
                        }
                    }
                }
            }

            for (String auth : split) {
                if (auth.lastIndexOf(" ") > 0) {
                    String a = auth.substring(0, auth.lastIndexOf(" ")).replace("-", "").replace(",", "").replace(".", "").replace(" ", "").trim();
                    String b = auth.substring(auth.lastIndexOf(" ")).replace("-", "").replace(",", "").replace(".", "").replace(" ", "").trim();
                    if (pinYinSet.contains(a) && !pinYinSet.contains(b) && b.length() > 3) {
                        // 对连在一起的字符串，判断是否是二个拼音构成，比如：ChengChang
                        for (int i = 2; i < b.length(); i++) {
                            String firstName = b.substring(0, i);
                            String lastName = b.substring(i);
                            if (pinYinSet.contains(firstName) && pinYinSet.contains(lastName)) {
//                                System.out.println("5 " + auth);
                                return false; // Chen, QiYue
                            }
                        }
                    }
                    if (pinYinSet.contains(b) && !pinYinSet.contains(a) && a.length() > 3) {
                        // 对连在一起的字符串，判断是否是二个拼音构成，比如：ChengChang
                        for (int i = 2; i < a.length(); i++) {
                            String firstName = a.substring(0, i);
                            String lastName = a.substring(i);
                            if (pinYinSet.contains(firstName) && pinYinSet.contains(lastName)) {
//                                System.out.println("6 " + auth);
                                return true; // QiYue Chen
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    public static void insertSetAndSum(String strAndSumTable, Map<Set<String>, Integer> ordinaryAndSumMap, String strField, String smuField) throws SQLException {
        Db.use().execute("truncate table " + strAndSumTable);
        ordinaryAndSumMap.keySet().parallelStream().forEach(set -> {
            try {
                Db.use().insert(Entity.create(strAndSumTable).set(strField, JSONUtil.toJsonStr(set)).set(smuField, ordinaryAndSumMap.get(set)));
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
    }

    public static void insertStrAndSum(String strAndSumTable, Map<String, Integer> ordinaryAndSumMap, String strField, String smuField) throws SQLException {
        Db.use().execute("truncate table " + strAndSumTable);
        ordinaryAndSumMap.keySet().parallelStream().forEach(str -> {
            try {
                Db.use().insert(Entity.create(strAndSumTable).set(strField, str).set(smuField, ordinaryAndSumMap.get(str)));
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
    }

    public static void addSetList(Map<String, String> originalAndMeshMap, List<String> keywordList, Set<List<String>> kwSetList, boolean limit) {
        for (String key : keywordList) {
            List<String> list = new ArrayList<>();
            key = key.replaceAll(" +", " ").trim();
            key = key.endsWith(".") ? key.substring(0, key.length() - 1) : key;
            if (key.length() > 1) {
                if (limit) {
                    if (key.length() > 12 || key.contains(" ")) {
                        list.add(key);
                        if (originalAndMeshMap.containsKey(key)) {
                            list.add(originalAndMeshMap.get(key));
                            list.add("m");
                        }
                        kwSetList.add(list);
                    }
                } else {
                    list.add(key);
                    if (originalAndMeshMap.containsKey(key)) {
                        list.add(originalAndMeshMap.get(key));
                        list.add("m");
                    }
                    kwSetList.add(list);
                }
            }
        }
    }

    public static Set<String> getCnAbKey(String abs, Map<String, String> keyWordAndStandardMap) {
        Set<String> abSet = new HashSet<>();
        if (null != abs && abs.length() > 1) {
            // 英文的点后面，如果是中文或字母给替换为空格，如果是数字则仍保留这个点，意思是0.05是不能切割的。
            String ab = abs.replace("；", " ").replace("，", ",").replace("。", " ").replace("、", " ").replace("（", " ").replace("）", " ").replace("【", " ")
                    .replace("：", " ").replace("！", " ").replace("？", " ").replace(";", " ").replaceAll("\\.([\u4e00-\u9fa5a-zA-Z])", " $1").replace("(", " ").replace("】", " ")
                    .replace(")", " ").replace(":", " ").replace("!", " ").replace("?", " ").replace(" ,", ",").replace(", ", ",").replaceAll(" +", " ").trim();
            String[] split = ab.split(" ");
            for (String s : split) {
                if (s.trim().length() > 4) {
                    extract(s, keyWordAndStandardMap, abSet);
                }
            }
        }
        return abSet;
    }

    // 从摘要中提取关键词，先以句号切割，再一句句提取。
    public static Set<String> getAbKey(String abs, Map<String, String> keyWordAndStandardMap) {
        String ab = abs.replace("? ", ". ").replace("; ", ". ").replace(": ", ". ").trim();
        Set<String> abSet = new HashSet<>();
        if (ab.length() > 11 && ab.contains(". ")) {
            ab = ab.replace("<p>", " ").replace("</p>", " ").replace("  ", " ").trim();
            ab = ab.endsWith(".") ? ab.substring(0, ab.length() - 1).trim() : ab.trim();
            String[] split = ab.split("\\. ");
            for (String s : split) {
                s = s.trim();
                s = s.endsWith(".") ? s.substring(0, s.length() - 1) : s;
                if (s.length() > 4 && s.contains(" ")) {
                    getBlanket(s, abSet);
                    extract(s, keyWordAndStandardMap, abSet);
                }
            }
        }
        return abSet;
    }

    // 从标题中提取关键词，部分标题中存在方括号，先给予去除。
    public static Set<String> getCnTiKey(String ti, Map<String, String> keyWordAndStandardMap) {
        Set<String> titleSet = new HashSet<>();
        if (null != ti && ti.length() > 4) {
            extract(ti, keyWordAndStandardMap, titleSet);
        }
        return titleSet;
    }

    // 从标题中提取关键词，部分标题中存在方括号，先给予去除。
    public static Set<String> getTiKey(String title, Map<String, String> keyWordAndStandardMap) {
        Set<String> titleSet = new HashSet<>();
        String ti = title.trim();
        if (ti.length() > 4 && ti.contains(" ")) {
            ti = ti.startsWith("[") ? ti.substring(1).trim() : ti.trim();
            ti = ti.endsWith(".") ? ti.substring(0, ti.length() - 1).trim() : ti.trim();
            ti = ti.endsWith("]") ? ti.substring(0, ti.length() - 1).trim() : ti.trim();
            ti = ti.endsWith(".") ? ti.substring(0, ti.length() - 1).trim() : ti.trim();
            getBlanket(ti, titleSet);
            extract(ti, keyWordAndStandardMap, titleSet);
        }
        return titleSet;
    }

    // 提取hepatocyte growth factor (HGF)->hepatocyte growth factor
    public static void getBlanket(String bracket, Set<String> abSet) {
        if (bracket.contains("(") && bracket.contains(")")) {
            bracket = bracket.replace("(", " (").replace(")", ") ").replace("-", "㵝纃 ").replaceAll(" +", " ").replace("( ", "(").replace(" )", ")").toLowerCase().trim();
            String[] list = bracket.split(" ");
            for (int i = 0; i < list.length; i++) {
                String s = list[i];
                if (s.startsWith("(") && s.endsWith(")") && s.length() > 3) {
                    String t = s.substring(1, s.length() - 1);
                    String[] split = t.split("");
                    if (i >= split.length) {
                        StringBuilder result = new StringBuilder();
                        boolean bool = true;
                        for (int j = 0; j < split.length; j++) {
                            if (list[i - split.length + j].startsWith(split[j])) {
                                result.append(list[i - split.length + j]).append(" ");
                            } else {
                                bool = false;
                            }
                        }
                        String r = result.toString().trim();
                        if (bool && r.contains(" ") && r.length() > 2) {
                            abSet.add(r.replace("㵝纃 ", "-").replace("㵝纃", "-"));
                        }
                    }
                }
            }
        }
    }

    // 执行提取，输入自由文本freeWords，标准词Map，取回提取到的集合extractSet，以及提取后的字符串remaining
    public static void extract(String freeWords, Map<String, String> keyWordAndStandardMap, Set<String> extractSet) {
//        System.out.println("freeWords_0:" + freeWords);
        StringBuilder remaining = new StringBuilder();
        if (null != freeWords && freeWords.length() > 1) {
            freeWords = reRemaining(freeWords).replaceAll("( non )", " non-").replaceAll("(^non )", "non-")
                    .replaceAll("( not )", " not-").replaceAll("(^not )", "not-")
                    .replace("非", "非-").replace("不", "不-").replaceAll("(.?-.?)", "㵝$1㵝");
            String spl = (Utils.isContainChinese(freeWords.replace("纃", "").replace("㵝", ""))) ? "" : " ";
            int splLen = spl.length();
            List<String> list = Arrays.asList(freeWords.split(spl));
            int iLen = 0;
            int listLen = list.size();
            for (int i = 0; i < listLen; i++) {
                if (freeWords.length() >= iLen) {
                    String iStr = freeWords.substring(iLen);
//                    System.out.println("iStr: " + iStr);
                    iLen += list.get(i).length() + splLen;
                    int jLen = 0;
                    for (int j = listLen - 1; j >= 0; j--) {
                        if (iStr.length() - jLen >= 0) {
                            String jStr = iStr.substring(0, iStr.length() - jLen);
//                            System.out.println("jStr: " + jStr);
                            jLen += list.get(j).length() + splLen;
                            String md5Name = (jStr.length() > 16) ? SecureUtil.md5(jStr).substring(8, 24).toLowerCase() : "";
                            if (keyWordAndStandardMap.containsKey(jStr) || (!md5Name.isEmpty() && keyWordAndStandardMap.containsKey(md5Name))) {
                                if ((Utils.isContainChinese(jStr) && extractChinese(freeWords, jStr)) || (!Utils.isContainChinese(jStr) && extractEnglish(freeWords, jStr))) {
                                    String mapValue = (jStr.length() > 16) ? keyWordAndStandardMap.get(md5Name) : keyWordAndStandardMap.get(jStr);
                                    String standardName = getStandardName(mapValue, jStr).replace("non-", "non ").replace("not-", "not ").replace("非-", "非").replace("不-", "不");
//                                    String original = resetRemaining(jStr.replace("㵝", ""));
                                    String s = resetRemaining(standardName);
                                    if (s.length() > 1) {
                                        extractSet.add(s);
                                    }
//                                    extractSet.add(original); // 上面的是标准的名称，下面的是原始的名称，可以只拿标准的，不拿原始的。
                                    iLen += (jStr.length() - list.get(i).length());
                                    i = i + jStr.split(spl).length - 1;
                                    break;
                                }
                            }
//                            System.out.println("~~" + jStr);
                            if (jStr.split(spl).length == 1) {
//                                System.out.println("--" + jStr + " freeWords:" + freeWords + " remaining:" + remaining);
//                                remaining.toString().substring(0,remaining.toString().length()-1)
                                remaining.append(jStr).append(spl);
                            }
                        }
                    }
                }
            }
        }

        // 下面的代码功能是，如果连续的两个字符在原字符串中，则不添加空格，若在则添加空格。比如：中华人民共和国万岁毛泽东万岁贾采用免疫组化SP法检测35例CA组织中STAT3和VEGF的表达新非小细胞大细胞 留下：贾 新 非 小 细，处理后返回：贾 新非小细
//        String f = null == freeWords ? "" : freeWords.replaceAll("㵝(.?)-(.?)㵝", "$1$2");
//        String r = resetRemaining(remaining.toString()).replaceAll("㵝(.?)-(.?)㵝", "$1$2").trim();
//        StringBuilder result = new StringBuilder();
//        if (!r.contains(" ") && f.length() > 1) {
//            String[] split = r.split("");
//            for (int i = 0; i < split.length; i++) {
//                if (split.length > i + 1) {
//                    if (f.contains(split[i] + split[i + 1])) {
//                        result.append(split[i]).append(split[i + 1]);
//                        i++;
//                    } else {
//                        result.append(split[i]).append(" ");
//                    }
//                }
//            }
//        }
//        return (result.length() == 0) ? r : result.toString();
    }

    // 获取标准的名称
    public static String getStandardName(String standard, String search) {
        standard = ("☷".equals(standard)) ? search : standard;
        standard = ("☷.".equals(standard)) ? (search + ".") : standard;
        standard = ("☷,".equals(standard)) ? (search + ",") : standard;
        standard = ("☷;".equals(standard)) ? (search + ";") : standard;
        standard = ("☷?".equals(standard)) ? (search + "?") : standard;
        standard = ("☷*".equals(standard)) ? (search + "*") : standard;
        standard = ("☷(".equals(standard)) ? ("(" + search + ").") : standard;
        standard = ("☷)".equals(standard)) ? ("(" + search + ")") : standard;
        standard = ("☷[".equals(standard)) ? ("[" + search + "].") : standard;
        standard = ("☷]".equals(standard)) ? ("[" + search + "]") : standard;
        standard = ("ㅐ".equals(standard)) ? resetRemaining(search) : standard;
        standard = ("ㅐ.".equals(standard)) ? (resetRemaining(search) + ".") : standard;
        standard = ("ㅐ,".equals(standard)) ? (resetRemaining(search) + ",") : standard;
        standard = ("ㅐ;".equals(standard)) ? (resetRemaining(search) + ";") : standard;
        standard = ("ㅐ?".equals(standard)) ? (resetRemaining(search) + "?") : standard;
        standard = ("ㅐ*".equals(standard)) ? (resetRemaining(search) + "*") : standard;
        standard = ("ㅐ(".equals(standard)) ? ("(" + search + ").") : standard;
        standard = ("ㅐ)".equals(standard)) ? ("(" + search + ")") : standard;
        standard = ("ㅐ[".equals(standard)) ? ("[" + search + "].") : standard;
        standard = ("ㅐ]".equals(standard)) ? ("[" + search + "]") : standard;
        return standard;
    }

    // 提取规则，英文的，a、开头且后面有空格或中文
    public static boolean backChinese(String total, String part) {
        if (total.startsWith(part + " ")) {
            return true;
        } else if (total.startsWith(part)) {
            Pattern p = Pattern.compile(escape(part) + "[\u4e00-\u9fa5]");
            return p.matcher(total).find();
        }
        return false;
    }

    // 提取规则，英文的，b、结尾且前面有空格或中文
    public static boolean frontChinese(String total, String part) {
        if (total.endsWith(" " + part)) {
            return true;
        } else if (total.endsWith(part)) {
            Pattern p = Pattern.compile("[\u4e00-\u9fa5]" + escape(part));
            return p.matcher(total).find();
        }
        return false;
    }

    // 提取规则，英文的，a、开头且后面有空格或中文；b、结尾且前面有空格或中文；c、前后均有空格或中文；
    public static boolean extractEnglish(String total, String part) {
        if (backChinese(total, part)) {
            return true;
        }
        if (frontChinese(total, part)) {
            return true;
        }
        if (total.contains(" " + part + " ")) {
            return true;
        }
        Pattern p = Pattern.compile("[\u4e00-\u9fa5]" + escape(part) + "[\u4e00-\u9fa5]");
        return p.matcher(total).find();
    }

    // 正则时，需要将普通字符串进行转义。
    public static String escape(String part) {
        return part.replace("\\", "\\\\").replace(".", "\\.").replace("^", "\\^").replace("$", "\\$").replace("(", "\\(").replace(")", "\\)")
                .replace("*", "\\*").replace("+", "\\+").replace("?", "\\?").replace("{", "\\{").replace("}", "\\}")
                .replace("[", "\\[").replace("]", "\\]").replace("|", "\\|");
    }

    // 提取规则，中文的，前面的不管，后面的，如果是中文或空格或以此结尾可以提取，如果是英文和数字，则不能提取。
    public static boolean extractChinese(String total, String part) {
        if (total.contains(part + " ")) {
            return true;
        }
        if (total.endsWith(part)) {
            return true;
        }
        Pattern p = Pattern.compile(escape(part) + "[\u4e00-\u9fa5]");
        return p.matcher(total).find();
    }

    public static void addKeySet(String keyword, Set<List<String>> keySet, Map<List<String>, Integer> keyWordSumMap, Map<String, Integer> cnKeyWordSumMap) {
        if (null != keyword && keyword.length() > 2) {
            String[] split = keyword.replace("（", "(").replace("）", ")").replace("；", "; ").replace("，", ",").replace("。", "; ")
                    .replaceAll(" +", " ").replace("( ", "(").replace(" )", ")").replace(" ,", ",").replace(", ", ",").split("; ");
            for (String keyWord : split) {
                boolean isChinese = Utils.isContainChinese(keyWord);
                if (isChinese) {
                    if (keyWord.length() > 1) {
                        // 内(Fracturefixation,internal)骨折固定术->内骨折固定术;(Fracturefixation,internal)
                        String str = keyWord.replaceAll("(.*)(\\(.*\\))(.*)", "$1$3ĺļ$2");
                        if (str.contains("ĺļ")) {
                            String[] strSplit = str.split("ĺļ");
                            if (split.length == 2) {
                                String s = strSplit[1].trim();
                                s = s.startsWith("(") && s.endsWith(")") ? s.substring("(".length(), s.length() - ")".length()) : s;
                                // 如果括号内是中文，忽略括号。如果是英文，再检查是全写，还是简写，如果符合要求，加入。
                                if (Utils.isContainChinese(s)) {
                                    cnKeyWordSumMap.compute(str, (key, value) -> value != null ? (value + 1) : 1);
                                } else {
                                    cnKeyWordSumMap.compute(strSplit[0], (key, value) -> value != null ? (value + 1) : 1);
                                    putKeyWordSumMap(s, keySet, keyWordSumMap);
                                }
                            } else {
                                cnKeyWordSumMap.compute(str.replace("ĺļ", ""), (key, value) -> value != null ? (value + 1) : 1);
                            }
                        } else {
                            cnKeyWordSumMap.compute(keyWord.trim(), (key, value) -> value != null ? (value + 1) : 1);
                        }
                    }
                } else {
                    putKeyWordSumMap(keyWord, keySet, keyWordSumMap);
                }
            }
        }
    }

    public static void putKeyWordSumMap(String keyWord, Set<List<String>> keySet, Map<List<String>, Integer> keyWordSumMap) {
        if (isKeyWordFull(keyWord) || isKeyWordAbbr(keyWord)) {
            keyWord = keyWord.trim();
            keyWord = keyWord.endsWith(".") ? keyWord.substring(0, keyWord.length() - 1) : keyWord;
            List<String> fullAbbrList = new ArrayList<>();
            // 全部是大写，或者不包含空格，首字母之后含有大写，可以认定为缩写词，缩写词是不改变大小写的。
            if (!Pattern.compile("[a-z]").matcher(keyWord).find() || (!keyWord.contains(" ") && Pattern.compile("[A-Z]").matcher(keyWord.substring(1)).find())) {
                fullAbbrList.add(keyWord.trim());
            } else {
                fullAbbrList.add(keyWord.toLowerCase().trim());
            }
            keySet.add(fullAbbrList);
            keyWordSumMap.compute(fullAbbrList, (key, value) -> value != null ? (value + 1) : 1);
        }
    }

    public static void insertTableAbb(String insertTable, int pmId, String ut, Set<List<String>> keySet, Set<List<String>> abbSet, String title, String keyword, String ab, int aid) {
        try {
            Db.use().insert(Entity.create(insertTable).set("aid", aid).set("pmId", pmId).set("ut", ut).set("key_word", JSONUtil.toJsonStr(keySet)).set("abb_word", JSONUtil.toJsonStr(abbSet)).set("ti", title).set("key", keyword).set("ab", ab));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 全写的关键词的认定标准，要求至少6位，且少于65位，且以空格切割小于10个单词。anaplastic lymphoma kinase-positive nonsmall cell lung cancer 这个字符串为61位
    public static boolean isKeyWordFull(String full) {
        return null != full && full.length() > 5 && full.length() < 65 && full.replace("-", " ").split(" ").length < 10;
    }

    // 如果缩写的单词为3位，要求必须全部是大写，且不含数字和空格，如果是4位或以上，则要求除首位外其它必须得有一个大写字母或包含"-"才可以。缩写的区分大小写，即6位以下的。
    public static boolean isKeyWordAbbr(String abbr) {
        if (null == abbr || abbr.length() < 3) {
            return false;
        }
        if (abbr.length() < 4) {
            return !Pattern.compile("[a-z0-9 ]").matcher(abbr).find();
        } else {
            return (Pattern.compile("[A-Z]").matcher(abbr.substring(1)).find() || abbr.contains("-"));
        }
    }

    // 正则判断是否包含大小写英文字母
    public static boolean isEnglish(String str) {
        Pattern p = Pattern.compile("[a-zA-Z]");
        Matcher m = p.matcher(str);
        return m.find();
    }

    public static void insert(String fullAbbrKeyWordTable, String original, String full, String abbr, int isOk, int aid, Integer pmId, String ut) {
        try {
            Db.use().insert(Entity.create(fullAbbrKeyWordTable).set("original", original).set("full", full).set("abbr", abbr).set("is_ok", isOk).set("aid", aid).set("is_ok", isOk).set("pmId", pmId).set("ut", ut).set("aid", aid).set("date", LocalDateTime.now()));
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 功能：查看全写的词组（full）与缩写的词组（abbr）之间，是否匹配。采用的方法是先循环full，从前往后推，再循环abbr，如果各单词的首字母是，则isOk++，若不是则退出，最后若isOk >= abbrSplit.length则判定是。
    public static String isEquals(String full, String abbr) {
        full = full.replace("\"", "");
        String[] fullSplit = full.split(" ");
        String[] abbrSplit = abbr.split("");
        int fLength = fullSplit.length;
        int aLength = abbrSplit.length;
        String result = "";
        if (fLength > aLength) {
            for (int i = 0; i < fullSplit.length; i++) {
                int isOk = 0;
                StringBuilder str = new StringBuilder();
                for (int j = 0; j < abbrSplit.length; j++) {
                    String a = abbrSplit[j];
                    if (fullSplit.length > i + j) {
                        if (fullSplit[i + j].startsWith(a)) {
                            isOk++;
                            str.append(fullSplit[i + j]).append(" ");
                        } else {
                            isOk = 0;
                            break;
                        }
                    }
                }
                result = isOk >= abbrSplit.length ? str.toString().trim() : result;
            }
            // 上面的方法是直接匹配，不允许中间有介词，下面的方法允许有介词。
            if (result.isEmpty()) {
                int add = 0;
                for (int i = 0; i < fullSplit.length; i++) {
                    int isOk = 0;
                    StringBuilder str = new StringBuilder();
                    for (int j = 0; j < abbrSplit.length; j++) {
                        String a = abbrSplit[j];
                        if (fullSplit.length > add + i + j) {
                            String f = fullSplit[add + i + j];
                            if (("a".equals(f) || "an".equals(f) || "of".equals(f) || "in".equals(f) || "on".equals(f) || "the".equals(f) || "and".equals(f) || "is".equals(f) || "at".equals(f) || "for".equals(f) || "with".equals(f) ||
                                    "by".equals(f) || "from".equals(f) || "to".equals(f) || "between".equals(f) || "among".equals(f) || "under".equals(f) || "above".equals(f) || "voor".equals(f))) {
                                add += 1;
                                // 为什么大于2？in national institutes of health stroke scale->NIHSS
                                if (add > 2) {
                                    str.append(f).append(" ");
                                }
                            }
                            if (fullSplit.length > add + i + j) {
                                if (fullSplit[add + i + j].startsWith(a)) {
                                    str.append(fullSplit[add + i + j]).append(" ");
                                    isOk++;
                                } else {
                                    isOk = 0;
                                    break;
                                }
                            }
                        }
                    }
                    result = isOk >= abbrSplit.length ? str.toString().trim() : result;
                }
            }
        }
//        System.out.println("_____full:" + full + " abbr:" + abbr + " result:" + result);
        return result.isEmpty() ? "false" : result;
    }

    public static void getCnAbbrKeyWord(String cnAb, String fullAbbrKeyWordTable, Integer aid, Set<List<String>> abbSet, Integer pmId, String ut, Map<List<String>, Integer> keyWordSumMap) {
        if (null != cnAb && cnAb.length() > 10) {
            cnAb = cnAb.replace("，", ", ").replaceAll(" +", " ").replace("（", "(").replace("）", ")").replace("( ", "(").replace(" )", ")").trim();
            if (cnAb.contains("(") && cnAb.contains(")")) {
                Matcher matcher = Pattern.compile("\\(([^()]+)\\)").matcher(cnAb);
                if (matcher.find()) {
                    while (matcher.find()) {
                        String s = matcher.group(1);
                        String[] split = s.split(",");
                        if (split.length == 2 && s.matches(".*[a-z].*[A-Z].*") && s.contains(",") && s.contains(" ") && !s.contains("=") && !s.matches(".*[\u4e00-\u9fa5].*")) {
                            String full = split[0];
                            String abbr = split[1];
                            String equals = fullAndAbbrEquals(full, abbr);
                            if (!"false".equals(equals)) {
                                full = ("true".equals(equals)) ? full : equals;
                                full = (full.startsWith("(") || full.startsWith(")")) ? full.substring(1) : full;
                                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                                full = full.startsWith("'") && full.endsWith("'") ? full.substring(1, full.length() - 1) : full;
//                                System.out.println("匹配成功 full:" + full + " abbr:" + abbr);
                                insert(fullAbbrKeyWordTable, s, full, abbr, 1, aid, pmId, ut);
                                if (isKeyWordFull(full) && isKeyWordAbbr(abbr)) {
                                    full = full.endsWith(".") ? full.substring(0, full.length() - 1) : full;
                                    abbr = abbr.endsWith(".") ? abbr.substring(0, abbr.length() - 1) : abbr;
                                    List<String> fullAbbrList = new ArrayList<>();
                                    fullAbbrList.add(full.toLowerCase());
                                    fullAbbrList.add(abbr);
                                    abbSet.add(fullAbbrList);
                                    List<String> fullList = new ArrayList<>();
                                    fullList.add(full.toLowerCase());
                                    keyWordSumMap.compute(fullList, (key, value) -> value != null ? (value + 1) : 1);
                                    keyWordSumMap.compute(fullAbbrList, (key, value) -> value != null ? (value + 1) : 1);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // 提取关键词，此类词的特征是前面是全写，后面有一个括号，比如：hepatocyte growth factor (HGF)->hepatocyte growth factor
    public static void getAbbrKeyWord(String paragraph, String fullAbbrKeyWordTable, Integer aid, Set<List<String>> abbSet, Integer pmId, String ut, Map<List<String>, Integer> keyWordSumMap) {
        if (null != paragraph && paragraph.contains("(") && paragraph.contains(")") && null != aid && aid > 0) {
            String[] paragraphSplit = paragraph.replace(".</p>", ". </p>").replace("<p>", " <p> ").replace("</strong>", " </strong> ").replace("<strong class", " <strong class")
                    .replace("/", " / ").replace("[", " [ ").replace("]", " ] ").split("\\. ");
            for (String str : paragraphSplit) {
                String[] sentenceSplit = str.split("\\)");
                for (String sentence : sentenceSplit) {
                    sentence += ")";
                    String original = sentence;
//                    System.out.println("original:" + original);
                    if (sentence.contains("(") && sentence.contains(")")) {
                        sentence = sentence.replace("(", " (").replace(")", ") ").replaceAll(" +", " ").replace(" -", "-")
                                .replace("- ", "-").replace("( ", "(").replace(" )", ")").trim();
                        String[] list = sentence.split(" ");
                        int end = 0;
                        for (int i = 0; i < list.length; i++) {
                            String s = list[i];
                            if (s.startsWith("(") && s.endsWith(")") && s.length() > 3) {
                                String abbr = s.substring(1, s.length() - 1);
                                if (isEnglish(abbr)) {
                                    int start = 0;
                                    boolean bool = true;
                                    for (int j = 0; j < i; j++) {
                                        if (end > start && sentence.length() > end) {
                                            String full = sentence.substring(start, end).trim();
                                            start += list[j].length() + 1;
                                            String equals = fullAndAbbrEquals(full, abbr);
                                            if (!"false".equals(equals)) {
                                                full = ("true".equals(equals)) ? full : equals;
                                                bool = false;
                                                full = (full.startsWith("(") || full.startsWith(")")) ? full.substring(1) : full;
                                                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                                                full = full.startsWith("'") && full.endsWith("'") ? full.substring(1, full.length() - 1) : full;
//                                                System.out.println("匹配成功 full:" + full + " abbr:" + abbr);
                                                insert(fullAbbrKeyWordTable, original, full, abbr, 1, aid, pmId, ut);
                                                if (isKeyWordFull(full) && isKeyWordAbbr(abbr)) {
                                                    full = full.endsWith(".") ? full.substring(0, full.length() - 1) : full;
                                                    abbr = abbr.endsWith(".") ? abbr.substring(0, abbr.length() - 1) : abbr;
                                                    List<String> fullAbbrList = new ArrayList<>();
                                                    fullAbbrList.add(full.toLowerCase());
                                                    fullAbbrList.add(abbr);
                                                    abbSet.add(fullAbbrList);
                                                    List<String> fullList = new ArrayList<>();
                                                    fullList.add(full.toLowerCase());
                                                    keyWordSumMap.compute(fullList, (key, value) -> value != null ? (value + 1) : 1);
                                                    keyWordSumMap.compute(fullAbbrList, (key, value) -> value != null ? (value + 1) : 1);
                                                }
                                                break;
                                            }
                                        }
                                    }
                                    if (bool) {
                                        insert(fullAbbrKeyWordTable, original, "", abbr, 0, aid, pmId, ut);
                                    }
                                }
                            }
                            end += list[i].length() + 1;
                        }
                    }
                }
            }
        }
    }

    // 判断词组与缩写的单词之间是否相等，如果词组内各单词的首字母与缩写的单词的各个首字母相等，或者首字母相等后半部分包含，可以认定为相等。比如：Mendelian randomization-Egger (MR-Egger)或hepatocyte growth factor (HGF)，要求全写的词组full必须包含空格，缩写的单词abbr必须大于1位，且首字母必须相同。
    public static String fullAndAbbrEquals(String full, String abbr) {
        if (null != full && null != abbr && (full.trim().contains(" ") || full.contains("-") || full.length() > 11) && abbr.length() > 1 && full.length() >= abbr.length()) {
            abbr = NumberUtil.isInteger(abbr.substring(0, 1)) ? abbr.substring(1) : abbr;
            // of 3-methoxy-O-desmethyl encainide (MODE)
            String[] fSplit = full.toLowerCase().split(" ");
            String[] aSplit = abbr.toLowerCase().split("");
            if (fSplit[0].contains("-" + aSplit[0]) && fSplit.length == 2 && aSplit.length == 4) {
                if (fSplit[0].contains("-" + aSplit[1]) && fSplit[0].contains("-" + aSplit[2]) && fSplit[1].startsWith(aSplit[3])) {
                    return full;
                }
            }
            // labelled with an anti-human leukocyte antigen (HLA)
            if (full.toLowerCase().contains("-" + abbr.substring(0, 1).toLowerCase())) {
                String[] fullSplit = full.toLowerCase().split(" ");
                String[] abbrSplit = abbr.toLowerCase().split("");
                int isOk = 0;
                String result = "";
                for (int i = 0; i < fullSplit.length; i++) {
                    int l = i + fullSplit.length - abbrSplit.length;
                    if (l > 0 && fullSplit.length > l && abbrSplit.length > i) {
                        String f = fullSplit[i + fullSplit.length - abbrSplit.length];
                        String a = abbrSplit[i];
                        if (f.contains("-" + a) || f.startsWith(a)) {
                            isOk++;
                            result += f + " ";
                        } else {
                            result = "";
                            isOk = 0;
                            break;
                        }
                    }
                }
                if (isOk >= abbrSplit.length) {
                    return result.trim();
                }
            }

            // Three-domain Interoceptive Sensations Questionnaire (THISQ)
            if (fSplit.length == 4 && aSplit.length == 5 && fSplit[0].contains("-")) {
                if (fSplit[0].startsWith(aSplit[0]) && fSplit[0].contains(aSplit[1]) && fSplit[1].startsWith(aSplit[2]) && fSplit[2].startsWith(aSplit[3]) && fSplit[3].startsWith(aSplit[4])) {
//                    System.out.println("3.2");
                    return "true";
                }
            }

            if (fSplit.length == aSplit.length) {
                boolean bool = true;
                for (int i = 0; i < fSplit.length; i++) {
                    if (!fSplit[i].substring(0, 1).equals(aSplit[i])) {
                        bool = false;
                        break;
                    }
                }
                if (bool) {
//                    System.out.println("2.1");
                    return "true";
                }
            }

            //In this study, reduced graphene oxide-TiO₂ (RGO-TiO₂)
            String[] fs = full.toLowerCase().split("-");
            String[] as = abbr.toLowerCase().split("-");
//            System.out.println("....~~~full:" + full + " abbr:" + abbr + " fs.length:" + fs.length + " as.length:" + as.length);
            if (fs.length == as.length && as.length == 2 && fs[1].equals(as[1]) && fs[1].length() > 1) {
                String[] f = fs[0].split(" ");
                String[] a = as[0].split("");
                if (f.length == a.length && f.length > 1) {
                    boolean bool = true;
                    for (int i = 0; i < f.length; i++) {
                        if (!f[i].substring(0, 1).equals(a[i])) {
                            bool = false;
                            break;
                        }
                    }
                    if (bool) {
//                        System.out.println("2.1");
                        return "true";
                    }
                }
            }

            if (fs.length == as.length && as.length == 3) {
                if (fs[0].startsWith(as[0]) && fs[1].startsWith(as[1])) {
//                    System.out.println("fs[2]:" + fs[2] + " as[2]:" + as[2]);
                    String[] f = fs[2].trim().split(" ");
                    String[] a = as[2].trim().split("");
                    if (f.length == a.length && f.length > 1) {
                        boolean bool = true;
                        for (int i = 0; i < f.length; i++) {
                            if (!f[i].substring(0, 1).equals(a[i])) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
//                            System.out.println("2.2");
                            return "true";
                        }
                    } else if (fs[2].trim().equals(as[2].trim())) {
//                        System.out.println("2.2");
                        return "true";
                    }
                }
            }

            full = full.startsWith("-") ? full.substring(1) : full;

            if (full.replace("(", "").replace("\"", "").replace("'", "").replace(")", "").substring(0, 1).equalsIgnoreCase(abbr.substring(0, 1))) {
                abbr = abbr.contains("α") && !full.contains("α") ? abbr.replace("α", " alpha ") : abbr;
                // Transforming growth factor-beta 1 (TGF-β1)
                abbr = abbr.contains("β") && !full.contains("β") ? abbr.replace("β", " beta ") : abbr;
                abbr = abbr.contains("γ") && !full.contains("γ") ? abbr.replace("γ", " gamma ") : abbr;
                abbr = abbr.contains("δ") && !full.contains("δ") ? abbr.replace("δ", " delta ") : abbr;
                abbr = abbr.contains("ε") && !full.contains("ε") ? abbr.replace("ε", " epsilon ") : abbr;
                abbr = abbr.contains("ζ") && !full.contains("ζ") ? abbr.replace("ζ", " zeta ") : abbr;
                abbr = abbr.contains("η") && !full.contains("η") ? abbr.replace("η", " eta ") : abbr;
                abbr = abbr.contains("θ") && !full.contains("θ") ? abbr.replace("θ", " theta ") : abbr;
                abbr = abbr.contains("ι") && !full.contains("ι") ? abbr.replace("ι", " iota ") : abbr;
                abbr = abbr.contains("κ") && !full.contains("κ") ? abbr.replace("κ", " kappa ") : abbr;
                abbr = abbr.contains("λ") && !full.contains("λ") ? abbr.replace("λ", " lambda ") : abbr;
                abbr = abbr.contains("μ") && !full.contains("μ") ? abbr.replace("μ", " mu ") : abbr;
                abbr = abbr.contains("ν") && !full.contains("ν") ? abbr.replace("ν", " nu ") : abbr;
                abbr = abbr.contains("ξ") && !full.contains("ξ") ? abbr.replace("ξ", " xi ") : abbr;
                abbr = abbr.contains("ο") && !full.contains("ο") ? abbr.replace("ο", " omicron ") : abbr;
                abbr = abbr.contains("π") && !full.contains("π") ? abbr.replace("π", " pi ") : abbr;
                abbr = abbr.contains("ρ") && !full.contains("ρ") ? abbr.replace("ρ", " rho ") : abbr;
                abbr = abbr.contains("σ") && !full.contains("σ") ? abbr.replace("σ", " sigma ") : abbr;
                abbr = abbr.contains("τ") && !full.contains("τ") ? abbr.replace("τ", " tau ") : abbr;
                abbr = abbr.contains("υ") && !full.contains("υ") ? abbr.replace("υ", " upsilon ") : abbr;
                abbr = abbr.contains("φ") && !full.contains("φ") ? abbr.replace("φ", " phi ") : abbr;
                abbr = abbr.contains("χ") && !full.contains("χ") ? abbr.replace("χ", " chi ") : abbr;
                abbr = abbr.contains("ψ") && !full.contains("ψ") ? abbr.replace("ψ", " psi ") : abbr;
                abbr = abbr.contains("ω") && !full.contains("ω") ? abbr.replace("ω", " omega ") : abbr;
                if (full.equalsIgnoreCase(abbr)) {
//                    System.out.println("1");
                    return "true";
                }

                // 'Miniature Inverted-repeat Transposable Element' -> MITE
                if (full.startsWith("'") && full.endsWith("'")) {
                    full = full.substring(1, full.length() - 1);
                    String[] fullSplit = full.split(" ");
                    String[] abbrSplit = abbr.split("");
                    if (fullSplit.length == abbrSplit.length) {
                        boolean bool = true;
                        for (int i = 0; i < fullSplit.length; i++) {
                            if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
//                            System.out.println("2");
                            return "true";
                        }
                    }
                }

                // "Miniature Inverted-repeat Transposable Element" -> MITE
                if (full.startsWith("\"") && full.endsWith("\"")) {
                    full = full.substring(1, full.length() - 1);
                    String[] fullSplit = full.split(" ");
                    String[] abbrSplit = abbr.split("");
                    if (fullSplit.length == abbrSplit.length) {
                        boolean bool = true;
                        for (int i = 0; i < fullSplit.length; i++) {
                            if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                                bool = false;
                                break;
                            }
                        }
                        if (bool) {
//                            System.out.println("2");
                            return "true";
                        }
                    }
                }

                // 如果缩写不包含"-"，则可以把全写词组中的给予替换成空格，若包含，则不可以，比如：autosomal-dominant PKD(ADPKD)
                full = full.replace("-", " ").replace("(", "").replace(")", "").toLowerCase().replaceAll(" +", " ").trim();
                full = full.startsWith("\"") && full.endsWith("\"") ? full.substring(1, full.length() - 1) : full;
                abbr = abbr.replace("-", "").toLowerCase().replaceAll(" +", " ").trim();
                String[] fullSplit = full.split(" ");
                String[] abbrSplit = abbr.split("");
//                System.out.println("~~~full:" + full + " abbr:" + abbr + " fullSplit.length:" + fullSplit.length + " abbrSplit.length:" + abbrSplit.length);

                // leucine phenylalanine   leuphe
                if (fullSplit.length == 2 && !abbr.contains(" ")) {
                    for (int i = abbr.length(); i > 0; i--) {
                        if (fullSplit[0].startsWith(abbr.substring(0, i))) {
                            if (fullSplit[1].startsWith(abbr.substring(i))) {
                                return "true";
                            }
                        }
                    }
                }

                // 如果词组的个数与缩写单词的个数相同，且词组内各单词的首字母与缩写的单词的各个首字母相等，使用下面的方法。
                if (fullSplit.length == abbrSplit.length) {
                    boolean bool = true;
                    for (int i = 0; i < fullSplit.length; i++) {
                        if (!fullSplit[i].substring(0, 1).equals(abbrSplit[i])) {
                            bool = false;
                            break;
                        }
                    }
                    if (bool) {
//                        System.out.println("2");
                        return "true";
                    }
                }
                // full:nanosecond pulsed electric field abbr:nsPEF
                if (fullSplit.length == 4 && abbrSplit.length == 5) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2]) && fullSplit[2].startsWith(abbrSplit[3]) && fullSplit[3].startsWith(abbrSplit[4])) {
//                        System.out.println("3");
                        return "true";
                    }
                    // full:|vascular smooth muscle cells| abbr:|VSMCs|
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].startsWith(abbrSplit[3]) && fullSplit[3].endsWith(abbrSplit[4])) {
//                        System.out.println("3.");
                        return "true";
                    }
                    //INTRODUCTION: Young pregnant and postpartum women living with HIV (WLHIV)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[3].equals(abbrSplit[2] + abbrSplit[3] + abbrSplit[4])) {
//                        System.out.println("3.4");
                        return "true";
                    }
                }
                // Long non-coding RNA (lncRNA)
                if (fullSplit.length == 4 && abbrSplit.length == 6) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].equals(abbrSplit[3] + abbrSplit[4] + abbrSplit[5])) {
//                        System.out.println("3.3");
                        return "true";
                    }
                }

                // The effects of a diet enriched with 25% raw soya bean flour (RSF)
                if (fullSplit.length == 4 && abbrSplit.length == 3) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[3].startsWith(abbrSplit[2])) {
//                        System.out.println("3.4");
                        return "true";
                    }
                }

                // full:pancreatic neuroendocrine tumors abbr:PanNETs
                if (fullSplit.length == 3 && abbr.length() > 3) {
                    boolean bool = true;
                    int sum = 0;
                    T:
                    for (int i = abbr.length(); i > 0; i--) {
                        if (fullSplit[0].startsWith(abbr.substring(0, i))) {
                            // 第一次，abbr是完整的，第二次时，abbr变成了nets
                            String second = abbr.substring(i);
                            for (int j = second.length(); j > 0; j--) {
                                if (fullSplit[1].startsWith(second.substring(0, j))) {
                                    // 第三次时，abbr变成了ts
                                    String third = second.substring(j);
                                    String[] split = third.split("");
                                    // 如果全部包含，则维持bool仍为true，否则有任一个不是则为false。为什么要加一个变量sum？因为bool的默认值是true，如果程序走不到这一步呢？这样bool仍为true，加上sum后，要求其必须执行++，就可以确保程序走到了这一步。
                                    for (String s : split) {
                                        if (fullSplit[2].contains(s)) {
                                            sum++;
                                        } else {
                                            bool = false;
                                            break T;
                                        }
                                    }
                                    break T;
                                }
                            }
                        }
                    }
                    if (bool && sum > 0) {
//                        System.out.println("4");
                        return "true";
                    }
                }

                // We report herein a rare case of malignant gastrointestinal stromal tumor (GIST)
                if (fullSplit.length == 3 && abbrSplit.length == 4) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2]) && fullSplit[2].startsWith(abbrSplit[3])) {
//                        System.out.println("4.1");
                        return "true";
                    }

                    // involvement of a ventral prefrontal cortex (VPFC)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2]) && fullSplit[2].startsWith(abbrSplit[3])) {
//                        System.out.println("4.2");
                        return "true";
                    }

                }
                // chemotherapy (CT)
                if (fullSplit.length == 1 && abbrSplit.length == 2) {
//                    System.out.println("5");
                    return fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) ? "true" : "false";
                }
                // full:lipopolysaccharide abbr:LPS
                if (fullSplit.length == 1 && abbrSplit.length == 3) {
//                    System.out.println("6");
                    return fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[0].contains(abbrSplit[1]) && fullSplit[0].contains(abbrSplit[2]) ? "true" : "false";
                }
                // interquartile range (IQR)
                if (fullSplit.length == 2 && abbrSplit.length == 3) {
                    if (fullSplit[0].startsWith(abbrSplit[0])) {
                        if (fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].startsWith(abbrSplit[2])) {
//                            System.out.println("7");
                            return "true";
                        }
                        if (fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2])) {
//                            System.out.println("8");
                            return "true";
                        }
                    }
                }

//                System.out.println("~~~full:" + full + " abbr:" + abbr + " fullSplit.length:" + fullSplit.length + " abbrSplit.length:" + abbrSplit.length);
                // the tissue non specific alkaline phosphatase (TNSALP)
                if (fullSplit.length == 5 && abbrSplit.length == 6) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].startsWith(abbrSplit[3]) && fullSplit[3].contains(abbrSplit[4]) && fullSplit[4].startsWith(abbrSplit[5])) {
//                        System.out.println("3.3");
                        return "true";
                    }
                    // ameliorates oxidative stress and dysfunctions of human retinal microvascular endothelial cells (hRMECs)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[2].startsWith(abbrSplit[2]) && fullSplit[3].startsWith(abbrSplit[3]) && fullSplit[4].startsWith(abbrSplit[4]) && fullSplit[4].endsWith(abbrSplit[5])) {
//                        System.out.println("3.4");
                        return "true";
                    }
                }

                // ameliorates oxidative stress and dysfunctions of human retinal microvascular endothelial cells (hRMECs)

                // full:pseudorandom sinusoidal abbr:prss
                if (fullSplit.length == 2 && abbrSplit.length == 4) {
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[2])) {
                        if (fullSplit[0].contains(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[3])) {
//                            System.out.println("9");
                            return "true";
                        }
                    }
                    // Considerable evidence has implicated matrix metalloproteinases (MMPs)
                    if (fullSplit[0].startsWith(abbrSplit[0]) && fullSplit[1].startsWith(abbrSplit[1]) && fullSplit[1].contains(abbrSplit[2]) && fullSplit[1].endsWith(abbrSplit[3])) {
//                        System.out.println("9.1");
                        return "true";
                    }
                }
                return isEquals(full, abbr);
            }
        }
        return "false";
    }


}
